# 产生式系统
from queue import Queue
class ProductionSystem:

    def __init__(self, DB, rules, animal):
        self.rules = rules
        self.animal = animal
        self.ks = []
        self.process = []
        self.DB = DB #综合数据库,例:[事实1，事实2...]
        self.KS = [] #可用知识集,例：[[条件1,条件2,...,结论],...]
        self.KB = rules #知识库，例：[[条件1,条件2,...,结论],...]
        self.target = []

    def forwardReasoning(self):
        DB = self.DB
        KS = self.KS
        KB = self.KB
        animal = self.animal
        flag = True
        while True:
            # DB中包含问题的解直接返回
            for fact in self.DB:
                if fact in animal:
                    return fact
            # 遍历知识库，找出可用知识
            for rule in KB:
                pre = rule[:-1] #取出前件
                for feature in pre:
                    if feature not in DB: #若前件与事实匹配不上，进入下一条规则匹配
                        break
                    if rule in self.KS: #已经有了这条知识,进入下一条知识匹配，防止重复匹配
                        break
                    if feature == pre[-1]: #前件匹配完成,将此知识送入KS
                        self.KS.append(rule)
            #得到其中一轮的可用知识库KS,循环遍历KS
            while KS:
                rule = KS.pop() #从可用知识库末尾取出一条知识
                fact = rule[-1]
                if fact not in self.DB: #新知识,加入综合数据库后更新KS
                    self.DB.append(fact)
                    self.process.append(f"匹配的规则是：{rule[:-1]} -> {fact}") #记录推理过程
                    flag = False #增加了新事实需要再次循环
                    break
                flag = True
            if len(KS) == 0 and flag:
                return "没能得到最终结果"

    #反向推理
    def backwardReasoning(self,animal):
        queue = Queue() #目标队列
        self.process.append(f"开始推理,目标是--->{animal}")
        #print(f"开始推理,目标是--->{animal}")
        self.target = []
        self.target.append(animal)
        queue.put(self.target)
        if self.backwardReasoning1(queue):#进入推理过程
             return f"推理成功--->{self.target[0]}"
        else:
            return "推理失败"

    #逆向推理过程
    def backwardReasoning1(self,queue):
        while not queue.empty(): #非空,进入循环
            target = queue.get() #取出第一个目标，例[虎]或["目标1","目标2"...]
            if self.isExist(target): #目标在事实库中
                self.process.append(f"已知条件中存在{str(target)}")
                #print(f"已知条件中存在{str(target)}")
                if queue.empty():
                    return True #目标队列为空推理成功
            else:
                for feature in target:
                    if self.isInitial(feature): #是原始条件
                        if feature not in self.DB: #是原始条件且不在综合数据库,直接下一个目标，因为这个前提必定不成立
                            if self.KS: #若不为空，取出一条知识(其实必定不为空，因为feature不是原始条件)
                                rule = self.KS.pop(-1) #得到知识
                                self.process.append(f"要证明{rule[-1]},需证明{rule[:-1]}")
                                #print(f"要证明{rule[-1]},需证明{rule[:-1]}")
                                queue.put(rule[:-1]) #得到新目标,入队
                            break
                            # if self.KS: #可用知识集不为空，再取
                            #     rule = self.KS.pop(-1)  # 得到知识
                            #     self.process.append(f"要证明{rule[-1]},需证明{rule[:-1]}")
                            #     #print(f"要证明{rule[-1]},需证明{rule[:-1]}")
                            #     queue.put(rule[:-1])
                            #     #print(f"暂时推不出{target}")
                            #     #break
                            # elif (not self.KS) and queue.empty():
                            #     self.process.append("没有补充就推理失败")
                            #     #print("没有补充就推理失败")
                            #     return False
                            # else:
                            #     pass
                        else:#是原始条件且在综合数据库
                            self.process.append(f"{feature}已经在综合数据库中")
                            #print(f"{feature}已经在综合数据库中")
                    else: #不是原始条件，检查可用知识集是否为空
                        if feature not in self.DB: #不在综合数据库才将其转换为子条件
                            self.getRelativeRule(feature)
                            if self.KS: #若不为空，取出一条知识(其实必定不为空，因为feature不是原始条件)
                                rule = self.KS.pop(-1) #得到知识
                                self.process.append(f"要证明{rule[-1]},需证明{rule[:-1]}")
                                #print(f"要证明{rule[-1]},需证明{rule[:-1]}")
                                queue.put(rule[:-1]) #得到新目标,入队
                            else:
                                print("推理失败hahaha")
                                return False
                        else:
                            self.process.append(f"{feature}已经在综合数据库中")
                            #print(f"{feature}已经在综合数据库中")
        return False


    #得到能导出结论的相应知识
    def getRelativeRule(self,conclusion):
        for rule in self.KB:
            if rule[-1] == conclusion:
                if rule not in self.KS:
                    self.KS.append(rule)  # 得到可用知识集

    #判断是否为初始条件
    def isInitial(self,feature):
        for rule in self.KB:
            if feature == rule[-1]:
                print(feature)
                return False
        return True

    #判断目标(列表类型)是否在事实库中
    def isExist(self,target):
        #print("system.DB",self.DB)
        for feature in target:
            if feature not in self.DB:
                return False
            if (feature == target[-1]) and (feature in self.DB):
                return True

